
#include "ws2812b.h"

/* WS2812B灯珠状态缓存数组定义 */
static ws2812b_color_t ws2812b_buffer[WS2812B_NUM] = {0};

#if (WS2812B_DRIVER_MODE == 0)  // nop延时实现
/* WS2812B端口初始化 */
void WS2812B_GPIO_Init(void)
{
    WS2812B_GPIO_RCC_ENABLE();
    
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Pin = WS2812B_PIN;
    GPIO_Init(WS2812B_PORT, &GPIO_InitStructure);
}

/* WS2812B初始化 */
void WS2812B_Init(void)
{
    WS2812B_GPIO_Init();    // 端口初始化
}

/* WS2812B发送码位 */
void WS2812B_SendCode(uint8_t code)
{
    // 发送1码
    if (code)
    {
        WS2812B_HIGH;   // 保持550~1200ns
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        
        WS2812B_LOW;    // 保持200~350ns
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
    }
    
    // 发送0码
    else
    {
        WS2812B_HIGH;   // 保持200~350ns
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        
        WS2812B_LOW;    // 保持550~1200ns
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
        __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
    }
}

/* WS2812B复位(一次传输结束) */
void WS2812B_Reset(void)
{
    WS2812B_LOW;    // 拉低电平
    for (uint16_t i = 0; i < 10000; i++) __NOP();   // 保持80us以上即可复位
}

/* 更新所有WS2812B灯珠的状态 */
void WS2812B_Update(void)
{
    for (uint16_t i = 0; i < WS2812B_NUM; i++)  // 根据灯的个数刷新
    {   // 三原色分开发
        for (uint8_t j = 0; j < 8; j++) WS2812B_SendCode((ws2812b_buffer[i].b.g << j) & 0x80);  // 发送G
        for (uint8_t j = 0; j < 8; j++) WS2812B_SendCode((ws2812b_buffer[i].b.r << j) & 0x80);  // 发送R
        for (uint8_t j = 0; j < 8; j++) WS2812B_SendCode((ws2812b_buffer[i].b.b << j) & 0x80);  // 发送B
    }
    
    WS2812B_Reset();    // 一次发送结束后复位
}

#else   // PWM、PWM+DMA实现
#define     HEAD_HOLD_LOW   (5) // 开始发数据前保持一段低电平
#define     TAIL_HOLD_LOW   (5) // 结束发数据后保持一段低电平

/* 存储颜色数据转换的PWM自动重装值 */
static uint16_t ws2812b_pwm_arr[HEAD_HOLD_LOW + 24*WS2812B_NUM + TAIL_HOLD_LOW] = {0};
#define     WS2812B_PWM_ARR_SIZE    (sizeof(ws2812b_pwm_arr) / sizeof(ws2812b_pwm_arr[0]))

/* WS2812B端口初始化 */
void WS2812B_GPIO_Init(void)
{
    WS2812B_GPIO_RCC_ENABLE();
    
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Pin = WS2812B_PIN;
    GPIO_Init(WS2812B_PORT, &GPIO_InitStructure);
}

/* WS2812B使用的PWM初始化 */
void WS2812B_PWM_Init(void)
{
    // 时钟使能
    WS2812B_TIM_RCC_ENABLE();
    
    // 配置时基单元
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = {0};
    TIM_TimeBaseStructure.TIM_Period = WS2812B_TIM_ARR; // ARR
    TIM_TimeBaseStructure.TIM_Prescaler = 0;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseInit(WS2812B_TIM, &TIM_TimeBaseStructure);
    
    // 配置输出比较
    TIM_OCInitTypeDef TIM_OCInitStructure = {0};
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_Pulse = 0;  // 初始占空比为0
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    TIM_OCInitStructure.TIM_OCIdleState = TIM_OCNIdleState_Reset;       // 设置空闲时输出低电平
    
#if (WS2812B_TIMER_TYPE)
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;    // 禁用互补输出通道
    TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;         // 设置互补输出通道极性高电平有效
    TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set;        // 设置互补通道空闲时输出高电平
#endif
    
    WS2812B_TIM_OCInit(WS2812B_TIM, &TIM_OCInitStructure);
    WS2812B_TIM_OCPreloadConfig(WS2812B_TIM, TIM_OCPreload_Enable);
    
    // 先关闭PWM输出
    TIM_Cmd(WS2812B_TIM, DISABLE);
//    TIM_Cmd(WS2812B_TIM, ENABLE);
    
#if (WS2812B_TIMER_TYPE)
    TIM_CtrlPWMOutputs(WS2812B_TIM, ENABLE);    // 使能高级定时器的PWM输出
#endif
}

#if (WS2812B_DRIVER_MODE == 2)
/* WS2812B使用的DMA初始化 */
void WS2812B_DMA_Init(void)
{
    // 使能时钟
    WS2812B_DMA_RCC_ENABLE();
    
    // 赋初始值
    DMA_DeInit(WS2812B_DMA_CH);
    
    // 配置DMA传输的参数
    DMA_InitTypeDef DMA_InitStructure = {0};                                            // 定义DMA初始化结构体
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&WS2812B_TIM->WS2812B_TIM_CH;  // 外设基地址配置为TIM2的CCR2地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ws2812b_pwm_arr;                  // 内部基地址配置为WS2812B数据缓冲区地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;                                  // 数据从缓冲区移到外设
    DMA_InitStructure.DMA_BufferSize = WS2812B_PWM_ARR_SIZE;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                     // 缓冲区索引自增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;                               // 正常模式，达到缓冲区大小后停止DMA传输
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;                       // 优先级，选择中等
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                                // 存储器到存储器，选择失能，数据由存储器转运到WS2812B外设
    DMA_Init(WS2812B_DMA_CH, &DMA_InitStructure);                               // 将结构体变量交给DMA_Init，配置DMA1的通道7

    // 使能定时器的DMA请求功能或更新事件
    TIM_DMACmd(WS2812B_TIM, WS2812B_DMA_SRC, ENABLE);
//    TIM_DMACmd(WS2812B_TIM, TIM_DMA_Update, ENABLE);
    
    // 先失能DMA
    DMA_Cmd(WS2812B_DMA_CH, DISABLE);
}
#endif

/* WS2812B初始化 */
void WS2812B_Init(void)
{
    WS2812B_GPIO_Init();    // 端口初始化
    WS2812B_PWM_Init();     // 定时器输出PWM初始化
    
#if (WS2812B_DRIVER_MODE == 2)
    WS2812B_DMA_Init();     // DMA初始化
#endif
}

/* WS2812B复位(一次传输结束) */
void WS2812B_Reset(void)
{
    for (uint16_t i = 0; i < 10000; i++) __NOP();   // 保持80us以上即可复位
}

/* 启动PWM控制 */
void WS2812B_PWM_Start(void)
{
    TIM_Cmd(WS2812B_TIM, ENABLE);
    for (uint32_t i = 0; i < WS2812B_PWM_ARR_SIZE; i++)
    {
        WS2812B_TIM->WS2812B_TIM_CH = ws2812b_pwm_arr[i];
        while (!(WS2812B_TIM->SR & TIM_SR_UIF));
        WS2812B_TIM->SR &= ~TIM_SR_UIF;
    }
    TIM_Cmd(WS2812B_TIM, DISABLE);
}

#if (WS2812B_DRIVER_MODE == 2)
/* 启动DMA传输 */
void WS2812B_DMA_TransmitStart(void)
{
    DMA_SetCurrDataCounter(WS2812B_DMA_CH, WS2812B_PWM_ARR_SIZE);   // 加载要传输的字节数
    TIM_Cmd(WS2812B_TIM, ENABLE);                                   // 使能TIM2
    DMA_Cmd(WS2812B_DMA_CH, ENABLE);                                // 使能DMA1通道7
    while (!DMA_GetFlagStatus(WS2812B_DMA_TCIF));                   // 等待传输完成
    DMA_Cmd(WS2812B_DMA_CH, DISABLE);                               // 失能DMA1通道7
    DMA_ClearFlag(WS2812B_DMA_TCIF);                                // 清除DMA1通道7传输完成标志位
    TIM_Cmd(WS2812B_TIM, DISABLE);                                  // 失能TIM2
}
#endif

/* 更新所有WS2812B灯珠的状态 */
void WS2812B_Update(void)
{
    for (uint16_t i = 0; i < WS2812B_NUM; i++)  // 根据灯的个数刷新
    {
        for (uint8_t j = 0; j < 8; j++) // 单个原色有8位
        {
            ws2812b_pwm_arr[HEAD_HOLD_LOW+i*24+j]
            = ((ws2812b_buffer[i].b.g << j) & 0x80) ? WS2812B_TIM_ARR_CODE1 : WS2812B_TIM_ARR_CODE0;    // 存储绿色数据
            ws2812b_pwm_arr[HEAD_HOLD_LOW+i*24+j+8]
            = ((ws2812b_buffer[i].b.r << j) & 0x80) ? WS2812B_TIM_ARR_CODE1 : WS2812B_TIM_ARR_CODE0;    // 存储红色数据
            ws2812b_pwm_arr[HEAD_HOLD_LOW+i*24+j+16]
            = ((ws2812b_buffer[i].b.b << j) & 0x80) ? WS2812B_TIM_ARR_CODE1 : WS2812B_TIM_ARR_CODE0;    // 存储蓝色数据
        }
    }
    
#if (WS2812B_DRIVER_MODE == 1)
    WS2812B_PWM_Start();            // 启动PWM控制
#endif
#if (WS2812B_DRIVER_MODE == 2)
    WS2812B_DMA_TransmitStart();    // 启动DMA传输
#endif
    WS2812B_Reset();                // 一次发送结束后复位
}
#endif

/**
  * @brief  设置WS2812B灯珠的颜色
  * @param  idx   灯珠索引(0 ~ WS2812B_NUM，小于0时表示控制全部灯珠)
  * @param  color 灯珠颜色
  * @param  lum   灯珠亮度(0~255)
  * @param  cover 覆盖属性(0:不覆盖，1:覆盖，选择覆盖后会清除除当前灯珠外其他灯珠的状态)
  * @retval NULL
  * @note   设置后还需调用更新函数才能真正地改变灯珠状态
  */
void WS2812B_SetColor(int16_t idx, uint32_t color, uint8_t lum, uint8_t cover)
{
#define SetLum(sc, hum) (sc * (hum) / 255.0f)   // 设置灯珠亮度
    
    if (idx >= WS2812B_NUM) return;
    ws2812b_color_t color_buf = {.rgb = color};
    
    if (idx >= 0)   // 控制单个灯珠
    {
        // 先设置单个灯珠颜色
        color_buf.b.r = SetLum(color_buf.b.r, lum);
        color_buf.b.g = SetLum(color_buf.b.g, lum);
        color_buf.b.b = SetLum(color_buf.b.b, lum);
        ws2812b_buffer[idx] = color_buf;
        
        // 若覆盖则清除其他灯珠颜色
        if (cover)
        {
            for (uint16_t i = 0; i < WS2812B_NUM; i++)
            {
                if (i == idx) continue;
                ws2812b_buffer[i].rgb = 0x000000;
            }
        }
    }
    else    // 控制所有灯珠
    {
        color_buf.b.r = SetLum(color_buf.b.r, lum);
        color_buf.b.g = SetLum(color_buf.b.g, lum);
        color_buf.b.b = SetLum(color_buf.b.b, lum);
        for (uint16_t i = 0; i < WS2812B_NUM; i++) ws2812b_buffer[i] = color_buf;
    }
}

/**
  * @brief  清除WS2812B灯珠的颜色
  * @param  idx 灯珠索引数组(0 ~ WS2812B_NUM，小于0时表示控制全部灯珠)
  * @retval NULL
  * @note   设置后还需调用更新函数才能真正地改变灯珠状态
  */
void WS2812B_ClearColor(int16_t idx)
{
    if (idx >= WS2812B_NUM) return;
    if (idx >= 0) ws2812b_buffer[idx].rgb = 0x000000;
    else
    {
        for (uint16_t i = 0; i < WS2812B_NUM; i++) ws2812b_buffer[i].rgb = 0x000000;
    }
}

/**
  * @brief  WS2812B灯珠测试
  * @param  NULL
  * @retval NULL
  * @note   主循环调用，该测试会小程度阻塞程序运行，建议仅在测试的时候使用
  */
void WS2812B_Test(void)
{
    for (uint32_t i = 0; i < SystemCoreClock / 10000; i++); // 阻塞延时约1ms
    
    const uint32_t test_colors[7] = {RGB888_RED, RGB888_ORANGE, RGB888_YELLOW, RGB888_GREEN, RGB888_CYAN, RGB888_BLUE, RGB888_PURPLE};
    static uint8_t color_idx = 0;
    static uint8_t color_lum = 0;
    static uint8_t breath_dir = 1;
    static uint8_t run_cnt = 0;
    
    if (++run_cnt >= 5)
    {
        run_cnt = 0;
        if (breath_dir) // 逐渐变亮
        {
            WS2812B_SetColor(-1, test_colors[color_idx], color_lum, 1);
            color_lum++;
            if (color_lum >= 100) breath_dir = 0;
        }
        else    // 逐渐变暗
        {
            WS2812B_SetColor(-1, test_colors[color_idx], color_lum, 1);
            color_lum--;
            if (color_lum <= 0)
            {
                color_idx = (color_idx+1) % 7;
                breath_dir = 1;
            }
        }
        WS2812B_Update();
    }
}
